#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <memory.h>
#include <malloc.h>
#include <errno.h>
#include <netinet/in.h>
#include "rfs-client.h"
int numread;

int enviar(PACKAGE *package, struct sockaddr_in sock, int flag) {
    return sendto(package->from, package, sizeof (PACKAGE), flag, (struct sockaddr *) &sock, sizeof (sock));
}

int recibir(PACKAGE *package, struct sockaddr_in sock, int flag) {
    int sock_size = sizeof (sock), n, i;
    // recibo el paquete
        errno = EAGAIN;
        n = -1;
        for (i = 0; errno == EAGAIN && i <= TIMEOUT && n < 0; i++) {
            n = recvfrom(package->from, package, sizeof(PACKAGE), flag, (struct sockaddr *) &sock, &sock_size);
            usleep(1000);
        }
        if (n < 0 && errno != EAGAIN) {
            printf("Error nº %i recibiendo paquete.\n", errno);
        } else
        if (n < 0 && errno == EAGAIN) {
            printf("Timeout recibiendo paquete.\n");
        }
    
    return n;
}

int
rls(PACKAGE *package) {
    struct sockaddr_in sock_from;
    // envío el comando
    package->op = RLS;
    enviar(package, dir, 0);

    // creo el puntero que va a contener el resultado final
    char * output = (char *) malloc(1);
    output[0] = '\0';

    do {
        // recibo el paquete
        if(recibir(package, sock_from, MSG_DONTWAIT) < 0 ) {
            output = (char *) realloc(output, 42);
            strcpy(output, "Ocurrió un error recibiendo la respuesta.");
            continue;
        } else {
            // agrego memoria para alojar el resultado
            output = (char *) realloc(output, strlen(output) + strlen(package->data));
            // agrego la cadena que viene con el paquete
            strcat(output, package->data);
            package->op = ACK;
            enviar(package, dir, 0);
        }
    } while (package->offset == DATA_LENGTH);

    // imprimo resultado
    printf("Resultado\n");
    printf("-----------------\n");
    printf("%s\n", output);
    printf("-----------------\n");

    // retorno cantidad de paquetes leídos
    return package->block_count;
}

int
rcd(PACKAGE *package) {

    struct sockaddr_in sock_from;
    // envío el comando
    package->op = RCD;
    enviar(package, dir, 0);

    // creo el puntero que va a contener el resultado final
    char * output = (char *) malloc(1);
    output[0] = '\0';

    if(recibir(package, sock_from, MSG_DONTWAIT) < 0 ) {
        output = (char *) realloc(output, 37+1);
        strcpy(output, "Ocurrió un error recibiendo el ACK.\n");
    } else
    if(package->op == ACK) {
        do {
            // recibo el paquete
            if(recibir(package, sock_from, MSG_DONTWAIT) < 0 ) {
                output = (char *) realloc(output, 43+1);
                strcpy(output, "Ocurrió un error recibiendo la respuesta.\n");
                continue;
            } else {
                // agrego memoria para alojar el resultado
                output = (char *) realloc(output, strlen(output) + strlen(package->data));
                // agrego la cadena que viene con el paquete
                strcat(output, package->data);
                package->op = ACK;
                enviar(package, dir, 0);
            }
        } while (package->offset == DATA_LENGTH);
    } else {
        output = (char *) realloc(output, 38+1);
        strcpy(output, "El servidor no reconoció el comando.\n");
    }

    // imprimo resultado
    printf("Resultado\n");
    printf("-----------------\n");
    printf("%s", output);
    printf("-----------------\n");

    // retorno cantidad de paquetes leídos
    return package->block_count;
}

int
get(PACKAGE *package) {

    struct sockaddr_in sock_from;
    FILE * file = NULL;

    // creo el puntero que va a contener el resultado final
    char * output = (char *) malloc(1);
    output[0] = '\0';

    char * file_name = (char *) malloc(DATA_LENGTH);
    strncpy(file_name, package->data, DATA_LENGTH);
    package->block_count = 0;
    do {
        printf("-- 1\n");
        package->op = GET;
        strncpy(package->data, file_name, DATA_LENGTH);
        printf("-- 2\n");
        enviar(package, dir, 0);

        printf("-- 3\n");
        if(recibir(package, sock_from, MSG_DONTWAIT) < 0 ) {
            output = (char *) realloc(output, 98+strlen(file_name)+1);
            sprintf(output, "Ocurrió un error al solicitar al servidor el archivo %s.\n"
                            "El servidor no respondió a la solicitud.\n",file_name);
            continue;
        } else
        if(package->op == OK) {
            printf("-- 4\n");
            if (file == NULL) {
                file = fopen(file_name, "w");
            }
            // escribo el bloque del lado del cliente
            fwrite(package->data, sizeof (char), package->offset, file);
            // preparo la llamada siguiente
            package->block_count++;
            // traigo el siguiente bloque
        } else {
            output = (char *) realloc(output, 122+strlen(file_name)+1);
            sprintf(output, "Ocurrió un error al intentar descargar el archivo.\n"
                "El archivo '%s' no existe o el servidor no tiene permiso para leerlo.\n",
                file_name);
            continue;
        }
    } while (package->offset == DATA_LENGTH);
    if (file != NULL)
        // cierro el archivo
        fclose(file);
    printf("-----------------\n");
    if (strlen(output) == 0) {
        int i = package->block_count;
        i--;
        i *= DATA_LENGTH;
        i += package->offset;
        output = (char *) realloc(output, 87+strlen(file_name)+sizeof(int)+1);
        sprintf(output, "Resultado:\nSe descargó correctamente el archivo '%s'.\n"
                "Se transmitieron %i bytes en total.\n",
                file_name, i);
    }
    printf("%s",output);
    printf("-----------------\n");

    free(file_name);
    return package->block_count;
}

int
put(PACKAGE *package) {
    int n;
    struct sockaddr_in sock_from;
    FILE * file = NULL;

    // creo el puntero que va a contener el resultado final
    char * output = (char *) malloc(1);
    output[0] = '\0';

    char * file_name = (char *) malloc(DATA_LENGTH);
    strncpy(file_name, package->data, DATA_LENGTH);
    package->block_count = 0;
    do {
        n = -1;
        package->op = PUT;
        strncpy(package->data, file_name, DATA_LENGTH);
        //envío comando y nombre de archivo
        enviar(package, dir, 0);
        // recibo confirmación
        if(recibir(package, sock_from, MSG_DONTWAIT) < 0 ) {
            output = (char *) realloc(output, 100+strlen(file_name)+1);
            sprintf(output, "Ocurrió un error al solicitar al servidor subir el archivo %s.\n"
                            "No se ha recibido el OK del servidor.\n", file_name);
            continue;
        } else
        // si no hubo error al crear el archivo en el servidor
        if (package->op == OK) {
            // ...lo abro en el cliente
            if (file == NULL) {
                file = fopen(file_name, "r");
            }
            // leo el bloque
            package->offset = fread(package->data, sizeof (char), DATA_LENGTH, file);
            // envio el bloque
            enviar(package, dir, 0);
            if(recibir(package, sock_from, MSG_DONTWAIT) < 0 ) {
                output = (char *) realloc(output, 37+1);
                strcpy(output, "Ocurrió un error recibiendo el ACK.\n");
                continue;
            } else {
                if(package->op == ACK) {
                    // preparo la llamada siguiente
                    package->block_count++;
                } else {
                    output = (char *) realloc(output, 47+1);
                    strcpy(output, "El servidor envió '%i' cuando se esperaba ACK.\n");
                    continue;
                }
            }
        } else {
            output = (char *) realloc(output, 101+strlen(file_name)+1);
            sprintf(output, "Ocurrió un error al solicitar al servidor subir el archivo %s.\n"
                            "El servidor no pudo crear el archivo.\n", file_name);
            continue;
        }
        // hasta que haya leído y enviado una porción menor al tamaño máximo (señal del fin de archivo)
    } while (package->offset == DATA_LENGTH);
    if (file != NULL)
        // cierro el archivo
        fclose(file);
    printf("-----------------\n");
    if (strlen(output) == 0) {
        int i = package->block_count;
        i--;
        i *= DATA_LENGTH;
        i += package->offset;
        output = (char *) realloc(output, 84+strlen(file_name)+sizeof(int)+1);
        sprintf(output, "Resultado:\nSe subió correctamente el archivo '%s'.\n"
                "Se transmitieron %i bytes en total.\n",
                file_name, i);
    }
    printf("%s",output);
    printf("-----------------\n");

    free(file_name);
    return package->block_count;
}

